package cn.com.tctec.faked;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;


public class CommonItem {

    private SimpleDateFormat datetimeFormat = new SimpleDateFormat(Constant.DATETIME_FMT);

    // 基本量
    private String tagId;
    private Date startTime;
    private Date endTime;

    // 每十五分钟固定增量 或 瞬时固定值
    private Double fixedValue;
    // 每十五分钟变化增量 或 瞬时变化值
    private Double varValue;
    // 是否是瞬时量
    private boolean instance;

    /**
     * 构造一个tagId为99998，每十五分钟增量在0.25±0.125范围内的增量模拟变量
     * CommonItem increaseItem = new CommonItem("99998", "2019-06-30 00:00:00", "2019-07-02 00:00:00", 0.25D, 0.125D, false);
     * increaseItem.print(DBEnum.MS_SQL, Constant.Statistic.ALL);
     *
     * 构造一个tagId为99997，数值在100±80范围内变化的瞬时模拟变量
     * CommonItem instanceItem = new CommonItem("99997", "2019-06-30 00:00:00", "2019-07-02 00:00:00", 100D, 80D, true);
     * instanceItem.geneString(DBEnum.INFLUX_DB, Constant.Statistic.DAY_AND_HIGH);
     *
     * @param tagId           tagId
     * @param startTimeStdStr start datetime string
     * @param endTimeStdStr   end datetime string
     * @param fixedValue      fixed increase per fifteen minutes or fixed value
     * @param varValue        var increase per fifteen minutes or var value
     * @param instance        是否是瞬时类型
     * @throws ParseException 解析起止日期时间字符串异常
     */
    public CommonItem(String tagId, String startTimeStdStr, String endTimeStdStr,
                      Double fixedValue, Double varValue, boolean instance)
            throws ParseException {
        super();
        this.tagId = tagId;
        this.startTime = new SimpleDateFormat(Constant.DATETIME_FMT).parse(startTimeStdStr);
        this.endTime = new SimpleDateFormat(Constant.DATETIME_FMT).parse(endTimeStdStr);
        this.fixedValue = fixedValue;
        this.varValue = varValue;
        this.instance = instance;
    }

    public void print(DBEnum dbEnum, int option) {
        Map<String, Map<String, CommonDatum>> statisticData = this.simulate();
        if((option & Constant.Statistic.QUARTER_HOUR) == Constant.Statistic.QUARTER_HOUR) {
            System.out.println();
            Map<String, CommonDatum> data = statisticData.get(Constant.Statistic.QUARTER_HOUR_MEASUREMENT_NAME);
            for(String key : data.keySet()) {
                System.out.println(data.get(key).geneInsertString(dbEnum));
            }
        }
        if((option & Constant.Statistic.HOUR) == Constant.Statistic.HOUR) {
            System.out.println();
            Map<String, CommonDatum> data = statisticData.get(Constant.Statistic.HOUR_MEASUREMENT_NAME);
            for(String key : data.keySet()) {
                System.out.println(data.get(key).geneInsertString(dbEnum));
            }
        }
        if((option & Constant.Statistic.BAN) == Constant.Statistic.BAN) {
            System.out.println();
            Map<String, CommonDatum> data = statisticData.get(Constant.Statistic.BAN_MEASUREMENT_NAME);
            for(String key : data.keySet()) {
                System.out.println(data.get(key).geneInsertString(dbEnum));
            }
        }
        if((option & Constant.Statistic.DAY) == Constant.Statistic.DAY) {
            System.out.println();
            Map<String, CommonDatum> data = statisticData.get(Constant.Statistic.DAY_MEASUREMENT_NAME);
            for(String key : data.keySet()) {
                System.out.println(data.get(key).geneInsertString(dbEnum));
            }
        }
        if((option & Constant.Statistic.WEEK) == Constant.Statistic.WEEK) {
            System.out.println();
            Map<String, CommonDatum> data = statisticData.get(Constant.Statistic.WEEK_MEASUREMENT_NAME);
            for(String key : data.keySet()) {
                System.out.println(data.get(key).geneInsertString(dbEnum));
            }
        }
        if((option & Constant.Statistic.MONTH) == Constant.Statistic.MONTH) {
            System.out.println();
            Map<String, CommonDatum> data = statisticData.get(Constant.Statistic.MONTH_MEASUREMENT_NAME);
            for(String key : data.keySet()) {
                System.out.println(data.get(key).geneInsertString(dbEnum));
            }
        }
        if((option & Constant.Statistic.QUARTER) == Constant.Statistic.QUARTER) {
            System.out.println();
            Map<String, CommonDatum> data = statisticData.get(Constant.Statistic.QUARTER_MEASUREMENT_NAME);
            for(String key : data.keySet()) {
                System.out.println(data.get(key).geneInsertString(dbEnum));
            }
        }
        if((option & Constant.Statistic.YEAR) == Constant.Statistic.YEAR) {
            System.out.println();
            Map<String, CommonDatum> data = statisticData.get(Constant.Statistic.YEAR_MEASUREMENT_NAME);
            for(String key : data.keySet()) {
                System.out.println(data.get(key).geneInsertString(dbEnum));
            }
        }

    }

    /**
     * @return {
     *     QUARTER_HOUR_MEASUREMENT_NAME : {
     *          "2019-01-01 02:15:00": CommonDatum
     *     },
     *     HOUR_MEASUREMENT_NAME: {
     *
     *     },
     *     BAN_MEASUREMENT_NAME: {
     *
     *     },
     *     DAY_MEASUREMENT_NAME: {
     *
     *     },
     *     WEEK_MEASUREMENT_NAME: {
     *
     *     },
     *     MONTH_MEASUREMENT_NAME: {
     *
     *     },
     *     QUARTER_MEASUREMENT_NAME: {
     *
     *     },
     *     YEAR_MEASUREMENT_NAME: {
     *
     *     }
     * }
     */
    private Map<String, Map<String, CommonDatum>> simulate() {
        Map<String, Map<String, CommonDatum>> statisticData = this.simulateDayAndLowerScale();
        Map<String, CommonDatum> dayData = statisticData.get(Constant.Statistic.DAY_MEASUREMENT_NAME);

        Map<String, CommonDatum> weekData = new HashMap<>();
        statisticData.put(Constant.Statistic.WEEK_MEASUREMENT_NAME, weekData);

        Map<String, CommonDatum> monthData = new HashMap<>();
        statisticData.put(Constant.Statistic.MONTH_MEASUREMENT_NAME, monthData);

        Map<String, CommonDatum> quarterData = new HashMap<>();
        statisticData.put(Constant.Statistic.QUARTER_MEASUREMENT_NAME, quarterData);

        Map<String, CommonDatum> yearData = new HashMap<>();
        statisticData.put(Constant.Statistic.YEAR_MEASUREMENT_NAME, yearData);

        Double weekSum = 0D;
        Double monthSum = 0D;
        Double quarterSum = 0D;
        Double yearSum = 0D;

        Calendar start = Calendar.getInstance();
        Calendar end = Calendar.getInstance();

        start.setTime(this.startTime);
        start.set(Calendar.MILLISECOND, 0);
        start.set(Calendar.SECOND, 0);
        start.set(Calendar.MINUTE, 0);
        start.set(Calendar.HOUR_OF_DAY, 0);
        end.setTime(endTime);
        while (end.after(start)) {
            // 取下一天为周期判断条件
            Calendar temp = Calendar.getInstance();
            temp.setTimeInMillis(start.getTimeInMillis());
            temp.add(Calendar.DATE, 1);

            int dayOfWeek = temp.get(Calendar.DAY_OF_WEEK);
            int dayOfMonth = temp.get(Calendar.DAY_OF_MONTH);
            int month = temp.get(Calendar.MONTH);
            int dayOfYear = temp.get(Calendar.DAY_OF_YEAR);

            CommonDatum dayDatum = dayData.get(datetimeFormat.format(start.getTime()));
            Double dayValue = dayDatum.getDeviation();
            weekSum += dayValue;
            monthSum += dayValue;
            quarterSum += dayValue;
            yearSum += dayValue;
            if (dayOfWeek == Constant.FIRST_DAY_OF_WEEK) {
                temp.add(Calendar.DATE, -7);
                CommonDatum datum = new CommonDatum(
                        this.tagId,
                        Constant.Statistic.WEEK_MEASUREMENT_NAME,
                        this.instance ? dayValue : weekSum, temp.getTimeInMillis() / 1000);
                weekData.put(datetimeFormat.format(temp.getTime()), datum);
                weekSum = 0D;
            }

            if (dayOfMonth == 1) {
                temp.add(Calendar.MONTH, -1);
                CommonDatum datum = new CommonDatum(
                        this.tagId,
                        Constant.Statistic.MONTH_MEASUREMENT_NAME,
                        this.instance ? dayValue : monthSum, temp.getTimeInMillis() / 1000);
                monthData.put(datetimeFormat.format(temp.getTime()), datum);
                monthSum = 0D;
            }

            if (dayOfMonth == 1 && month % 3 == 0) {
                temp.add(Calendar.MONTH, -3);
                CommonDatum datum = new CommonDatum(
                        this.tagId,
                        Constant.Statistic.QUARTER_MEASUREMENT_NAME,
                        this.instance ? dayValue : quarterSum, temp.getTimeInMillis() / 1000);
                quarterData.put(datetimeFormat.format(temp.getTime()), datum);
                quarterSum = 0D;
            }

            if (dayOfYear == 1) {
                temp.add(Calendar.YEAR, -1);
                CommonDatum datum = new CommonDatum(
                        this.tagId,
                        Constant.Statistic.DAY_MEASUREMENT_NAME,
                        this.instance ? dayValue : weekSum, temp.getTimeInMillis() / 1000);
                yearData.put(datetimeFormat.format(temp.getTime()), datum);
                weekSum = 0D;
            }

            start.add(Calendar.DATE, 1);
        }

        int dayOfWeek = start.get(Calendar.DAY_OF_WEEK);
        int dayOfMonth = start.get(Calendar.DAY_OF_MONTH);
        int month = start.get(Calendar.MONTH);
        int dayOfYear = start.get(Calendar.DAY_OF_YEAR);

        double increase = this.fixedValue + this.varValue * new Random().nextFloat();

        if (dayOfWeek != Constant.FIRST_DAY_OF_WEEK) {
            start.set(Calendar.DAY_OF_WEEK, Constant.FIRST_DAY_OF_WEEK);
            CommonDatum datum = new CommonDatum(
                    this.tagId,
                    Constant.Statistic.WEEK_MEASUREMENT_NAME,
                    this.instance ? increase : weekSum, start.getTimeInMillis() / 1000);
            weekData.put(datetimeFormat.format(start.getTime()), datum);
        }

        if (dayOfMonth != 1) {
            start.set(Calendar.DAY_OF_MONTH, 1);
            CommonDatum datum = new CommonDatum(
                    this.tagId,
                    Constant.Statistic.MONTH_MEASUREMENT_NAME,
                    this.instance ? increase : monthSum, start.getTimeInMillis() / 1000);
            monthData.put(datetimeFormat.format(start.getTime()), datum);
        }

        if (dayOfMonth != 1 || month % 3 != 0) {
            month = month / 3 * 3;
            start.set(Calendar.DAY_OF_MONTH, 1);
            start.set(Calendar.MONTH, month);
            CommonDatum datum = new CommonDatum(
                    this.tagId,
                    Constant.Statistic.QUARTER_MEASUREMENT_NAME,
                    this.instance ? increase : quarterSum, start.getTimeInMillis() / 1000);
            quarterData.put(datetimeFormat.format(start.getTime()), datum);
        }

        if (dayOfYear != 1) {
            start.set(Calendar.DAY_OF_YEAR, 1);
            CommonDatum datum = new CommonDatum(
                    this.tagId,
                    Constant.Statistic.YEAR_MEASUREMENT_NAME,
                    this.instance ? increase : yearSum, start.getTimeInMillis() / 1000);
            yearData.put(datetimeFormat.format(start.getTime()), datum);
        }

        return statisticData;
    }


    /**
     * @return {
     *     Constant.Statistic.QUARTER_HOUR_MEASUREMENT_NAME : {
     *          "2019-01-01 02:15:00": CommonDatum
     *     },
     *     HOUR_MEASUREMENT_NAME: {
     *
     *     },
     *     BAN_MEASUREMENT_NAME: {
     *
     *     },
     *     Constant.Statistic.DAY_MEASUREMENT_NAME: {
     *
     *     }
     * }
     */
    private Map<String, Map<String, CommonDatum>> simulateDayAndLowerScale() {

        Map<String, Map<String, CommonDatum>> statisticData = new HashMap<>();

        Map<String, CommonDatum> dayData = new HashMap<>();
        statisticData.put(Constant.Statistic.DAY_MEASUREMENT_NAME, dayData);

        Map<String, CommonDatum> banData = new HashMap<>();
        statisticData.put(Constant.Statistic.BAN_MEASUREMENT_NAME, banData);

        Map<String, CommonDatum> hourData = new HashMap<>();
        statisticData.put(Constant.Statistic.HOUR_MEASUREMENT_NAME, hourData);

        Map<String, CommonDatum> fiftyMinutesData = new HashMap<>();
        statisticData.put(Constant.Statistic.QUARTER_HOUR_MEASUREMENT_NAME, fiftyMinutesData);

        Calendar start = Calendar.getInstance();
        Calendar end = Calendar.getInstance();

        start.setTime(this.startTime);
        start.set(Calendar.MILLISECOND, 0);
        start.set(Calendar.SECOND, 0);
        start.set(Calendar.MINUTE, 0);
        end.setTime(endTime);

        double hourSum = 0D;
        double banSum = 0D;
        double daySum = 0D;
        // 处理天、班、时和一刻钟的统计
        while (end.after(start)) {
            int minute = start.get(Calendar.MINUTE);
            int hour = start.get(Calendar.HOUR_OF_DAY);

            double increase = this.fixedValue + this.varValue * new Random().nextFloat();
            hourSum += increase;
            banSum += increase;
            daySum += increase;

            CommonDatum q_h_datum = new CommonDatum(
                    this.tagId,
                    Constant.Statistic.QUARTER_HOUR_MEASUREMENT_NAME,
                    increase, start.getTimeInMillis() / 1000);
            fiftyMinutesData.put(datetimeFormat.format(start.getTime()), q_h_datum);

            if (minute == 45) {
                Calendar temp = Calendar.getInstance();
                temp.setTimeInMillis(start.getTimeInMillis());
                temp.set(Calendar.MINUTE, 0);
                CommonDatum hour_datum = new CommonDatum(
                        this.tagId,
                        Constant.Statistic.HOUR_MEASUREMENT_NAME,
                        this.instance ? increase : hourSum, temp.getTimeInMillis() / 1000);
                hourData.put(datetimeFormat.format(temp.getTime()), hour_datum);
                hourSum = 0D;
            }

            if (minute == 45 && (hour == 7 || hour == 15 || hour == 23)) {
                Calendar temp = Calendar.getInstance();
                temp.setTimeInMillis(start.getTimeInMillis());
                temp.set(Calendar.MINUTE, 0);
                temp.set(Calendar.HOUR_OF_DAY, hour - 7);
                CommonDatum ban_datum = new CommonDatum(
                        this.tagId,
                        Constant.Statistic.BAN_MEASUREMENT_NAME,
                        this.instance ? increase : banSum, temp.getTimeInMillis() / 1000);
                banData.put(datetimeFormat.format(temp.getTime()), ban_datum);
                banSum = 0D;
            }

            if (minute == 45 && hour == 23) {
                Calendar temp = Calendar.getInstance();
                temp.setTimeInMillis(start.getTimeInMillis());
                temp.set(Calendar.MINUTE, 0);
                temp.set(Calendar.HOUR_OF_DAY, 0);
                CommonDatum day_datum = new CommonDatum(
                        this.tagId,
                        Constant.Statistic.DAY_MEASUREMENT_NAME,
                        this.instance ? increase : daySum, temp.getTimeInMillis() / 1000);
                dayData.put(datetimeFormat.format(temp.getTime()), day_datum);
                daySum = 0D;
            }

            start.add(Calendar.MINUTE, 15);
        }

        int minute = start.get(Calendar.MINUTE);
        int hour = start.get(Calendar.HOUR_OF_DAY);
        double increase = this.fixedValue + this.varValue * new Random().nextFloat();


        if (minute != 0) {
            Calendar temp = Calendar.getInstance();
            temp.setTimeInMillis(start.getTimeInMillis());
            temp.set(Calendar.MINUTE, 0);
            CommonDatum hour_datum = new CommonDatum(
                    this.tagId,
                    Constant.Statistic.HOUR_MEASUREMENT_NAME,
                    this.instance ? increase : hourSum, temp.getTimeInMillis() / 1000);
            hourData.put(datetimeFormat.format(temp.getTime()), hour_datum);
        }

        if (minute != 0 || hour % 8 != 0) {
            Calendar temp = Calendar.getInstance();
            temp.setTimeInMillis(start.getTimeInMillis());
            hour = hour / 8 * 8;
            temp.set(Calendar.MINUTE, 0);
            temp.set(Calendar.HOUR_OF_DAY, hour);
            CommonDatum ban_datum = new CommonDatum(
                    this.tagId,
                    Constant.Statistic.BAN_MEASUREMENT_NAME,
                    this.instance ? increase : banSum, temp.getTimeInMillis() / 1000);
            banData.put(datetimeFormat.format(temp.getTime()), ban_datum);
        }

        if (minute != 0 || hour != 0) {
            Calendar temp = Calendar.getInstance();
            temp.setTimeInMillis(start.getTimeInMillis());
            temp.set(Calendar.MINUTE, 0);
            temp.set(Calendar.HOUR_OF_DAY, 0);
            CommonDatum day_datum = new CommonDatum(
                    this.tagId,
                    Constant.Statistic.DAY_MEASUREMENT_NAME,
                    this.instance ? increase : daySum, temp.getTimeInMillis() / 1000);
            dayData.put(datetimeFormat.format(temp.getTime()), day_datum);
        }

        return statisticData;
    }
}
